﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace botliga
{
    public class CBNETProtocol
    {
        public const Byte BNET_HEADER_CONSTANT = 255;
        public enum Protocol
        {
            SID_NULL = 0,	// 0x0
            SID_STOPADV = 2,	// 0x2
            SID_GETADVLISTEX = 9,	// 0x9
            SID_ENTERCHAT = 10,	// 0xA
            SID_JOINCHANNEL = 12,	// 0xC
            SID_CHATCOMMAND = 14,	// 0xE
            SID_CHATEVENT = 15,	// 0xF
            SID_CHECKAD = 21,	// 0x15
            SID_STARTADVEX3 = 28,	// 0x1C
            SID_DISPLAYAD = 33,	// 0x21
            SID_NOTIFYJOIN = 34,	// 0x22
            SID_PING = 37,	// 0x25
            SID_LOGONRESPONSE = 41,	// 0x29
            SID_NETGAMEPORT = 69,	// 0x45
            SID_AUTH_INFO = 80,	// 0x50
            SID_AUTH_CHECK = 81,	// 0x51
            SID_AUTH_ACCOUNTLOGON = 83,	// 0x53
            SID_AUTH_ACCOUNTLOGONPROOF = 84,	// 0x54
            SID_WARDEN = 94,	// 0x5E
            SID_FRIENDSLIST = 101,	// 0x65
            SID_FRIENDSUPDATE = 102,	// 0x66
            SID_CLANMEMBERLIST = 125,	// 0x7D
            SID_CLANMEMBERSTATUSCHANGE = 127	// 0x7F
        };
        public enum KeyResult
        {
            KR_GOOD = 0,
            KR_OLD_GAME_VERSION = 256,
            KR_INVALID_VERSION = 257,
            KR_ROC_KEY_IN_USE = 513,
            KR_TFT_KEY_IN_USE = 529
        };
        public enum IncomingChatEvent
        {
            EID_SHOWUSER = 1,	// received when you join a channel (includes users in the channel and their information)
            EID_JOIN = 2,	// received when someone joins the channel you're currently in
            EID_LEAVE = 3,	// received when someone leaves the channel you're currently in
            EID_WHISPER = 4,	// received a whisper message
            EID_TALK = 5,	// received when someone talks in the channel you're currently in
            EID_BROADCAST = 6,	// server broadcast
            EID_CHANNEL = 7,	// received when you join a channel (includes the channel's name, flags)
            EID_USERFLAGS = 9,	// user flags updates
            EID_WHISPERSENT = 10,	// sent a whisper message
            EID_CHANNELFULL = 13,	// channel is full
            EID_CHANNELDOESNOTEXIST = 14,	// channel does not exist
            EID_CHANNELRESTRICTED = 15,	// channel is restricted
            EID_INFO = 18,	// broadcast/information message
            EID_ERROR = 19,	// error message
            EID_EMOTE = 23	// emote
        };
        List<Byte> m_ClientToken;			// set in constructor
        List<Byte> m_LogonType;				// set in RECEIVE_SID_AUTH_INFO
        List<Byte> m_ServerToken;			// set in RECEIVE_SID_AUTH_INFO
        List<Byte> m_MPQFileTime;			// set in RECEIVE_SID_AUTH_INFO
        List<Byte> m_IX86VerFileName;		// set in RECEIVE_SID_AUTH_INFO
        List<Byte> m_ValueStringFormula;		// set in RECEIVE_SID_AUTH_INFO
        List<Byte> m_KeyState;				// set in RECEIVE_SID_AUTH_CHECK
        List<Byte> m_KeyStateDescription;	// set in RECEIVE_SID_AUTH_CHECK
        List<Byte> m_Salt;					// set in RECEIVE_SID_AUTH_ACCOUNTLOGON
        List<Byte> m_ServerPublicKey;		// set in RECEIVE_SID_AUTH_ACCOUNTLOGON
        List<Byte> m_UniqueName;				// set in RECEIVE_SID_ENTERCHAT

        public List<Byte> GetClientToken() { return m_ClientToken; }
        public List<Byte> GetLogonType() { return m_LogonType; }
        public List<Byte> GetServerToken() { return m_ServerToken; }
        public List<Byte> GetMPQFileTime() { return m_MPQFileTime; }
        public List<Byte> GetIX86VerFileName() { return m_IX86VerFileName; }
        public string GetIX86VerFileNameString() { return ASCIIEncoding.ASCII.GetString(m_IX86VerFileName.ToArray()); }
        public List<Byte> GetValueStringFormula() { return m_ValueStringFormula; }
        public string GetValueStringFormulaString() { return ASCIIEncoding.ASCII.GetString(m_ValueStringFormula.ToArray()); }
        public List<Byte> GetKeyState() { return m_KeyState; }
        public string GetKeyStateDescription() { return ASCIIEncoding.ASCII.GetString(m_KeyStateDescription.ToArray()); }
        public List<Byte> GetSalt() { return m_Salt; }
        public List<Byte> GetServerPublicKey() { return m_ServerPublicKey; }
        public List<Byte> GetUniqueName() { return m_UniqueName; }

        public CBNETProtocol()
        {

            Byte[] ClientToken = { 220, 1, 203, 7 };
            m_ClientToken = Util.UTIL_CreateByteArray(ClientToken, 4);

        }


        #region RECEIVE FUNCTION

        public bool RECEIVE_SID_NULL(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_NULL" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length

            return ValidateLength(data);
        }
        public bool RECEIVE_SID_ENTERCHAT(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_ENTERCHAT" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // null terminated string	-> UniqueName

            if (ValidateLength(data) && data.Count >= 5)
            {
                m_UniqueName = Util.UTIL_ExtractCString(data, 4);
                return true;
            }

            return false;
        }
        public CIncomingChatEvent RECEIVE_SID_CHATEVENT(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_CHATEVENT" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> EventID
            // 4 bytes					-> ???
            // 4 bytes					-> Ping
            // 12 bytes					-> ???
            // null terminated string	-> User
            // null terminated string	-> Message

            if (ValidateLength(data) && data.Count >= 29)
            {
                List<Byte> EventID = data.GetRange(4, 4);
                List<Byte> Ping = data.GetRange(12, 4);
                List<Byte> User = Util.UTIL_ExtractCString(data, 28);
                List<Byte> Message = Util.UTIL_ExtractCString(data, (uint)User.Count + 29);

                switch (Util.UTIL_ByteArrayToUInt32(EventID, false))
                {
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_SHOWUSER:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_JOIN:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_LEAVE:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_WHISPER:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_TALK:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_BROADCAST:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_CHANNEL:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_USERFLAGS:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_WHISPERSENT:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_CHANNELFULL:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_CHANNELDOESNOTEXIST:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_CHANNELRESTRICTED:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_INFO:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_ERROR:
                    case (uint)CBNETProtocol.IncomingChatEvent.EID_EMOTE:
                        return new CIncomingChatEvent((CBNETProtocol.IncomingChatEvent)Util.UTIL_ByteArrayToUInt32(EventID, false),
                                                            (int)Util.UTIL_ByteArrayToUInt32(Ping, false),
                                                            ASCIIEncoding.ASCII.GetString(User.ToArray()),
                                                            ASCIIEncoding.ASCII.GetString(Message.ToArray())
                                                            );
                }

            }

            return null;
        }
        public bool RECEIVE_SID_CHECKAD(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_CHECKAD" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length

            return ValidateLength(data);
        }

        public bool RECEIVE_SID_STARTADVEX3(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_STARTADVEX3" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> Status

            if (ValidateLength(data) && data.Count >= 8)
            {
                List<Byte> Status = data.GetRange(4, 4);

                if (Util.UTIL_ByteArrayToUInt32(Status, false) == 0)
                    return true;
            }

            return false;
        }
        public List<Byte> RECEIVE_SID_PING(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_PING" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> Ping

            if (ValidateLength(data) && data.Count >= 8)
                return data.GetRange(4, 4);

            return new List<Byte>();
        }
        public bool RECEIVE_SID_LOGONRESPONSE(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_LOGONRESPONSE" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> Status

            if (ValidateLength(data) && data.Count >= 8)
            {
                List<Byte> Status = data.GetRange(4,4);

                if (Util.UTIL_ByteArrayToUInt32(Status, false) == 1)
                    return true;
            }

            return false;
        }

        public bool RECEIVE_SID_AUTH_INFO(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_AUTH_INFO" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> LogonType
            // 4 bytes					-> ServerToken
            // 4 bytes					-> ???
            // 8 bytes					-> MPQFileTime
            // null terminated string	-> IX86VerFileName
            // null terminated string	-> ValueStringFormula

            if (ValidateLength(data) && data.Count >= 25)
            {
                m_LogonType = data.GetRange(4, 4);
                m_ServerToken = data.GetRange(8, 4);
                m_MPQFileTime = data.GetRange(16,8);
                m_IX86VerFileName = Util.UTIL_ExtractCString(data, 24);
                m_ValueStringFormula = Util.UTIL_ExtractCString(data,(uint)m_IX86VerFileName.Count + 25);
                return true;
            }

            return false;
        }
        public bool RECEIVE_SID_AUTH_CHECK(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_AUTH_CHECK" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> KeyState
            // null terminated string	-> KeyStateDescription

            if (ValidateLength(data) && data.Count >= 9)
            {
                m_KeyState = data.GetRange(4,4);
                m_KeyStateDescription = Util.UTIL_ExtractCString(data, 8);

                if (Util.UTIL_ByteArrayToUInt32(m_KeyState, false) == (uint)KeyResult.KR_GOOD)
                    return true;
            }

            return false;
        }
        public bool RECEIVE_SID_AUTH_ACCOUNTLOGON(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_AUTH_ACCOUNTLOGON" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> Status
            // if( Status == 0 )
            //		32 bytes			-> Salt
            //		32 bytes			-> ServerPublicKey

            if (ValidateLength(data) && data.Count >= 8)
            {
                List<Byte> status = data.GetRange(4,4);

                if (Util.UTIL_ByteArrayToUInt32(status, false) == 0 && data.Count >= 72)
                {
                    m_Salt = data.GetRange(8, 32);
                    m_ServerPublicKey = data.GetRange(40, 32);
                    return true;
                }
            }

            return false;
        }
        public bool RECEIVE_SID_AUTH_ACCOUNTLOGONPROOF(List<Byte> data)
        {
            // DEBUG_Print( "RECEIVED SID_AUTH_ACCOUNTLOGONPROOF" );
            // DEBUG_Print( data );

            // 2 bytes					-> Header
            // 2 bytes					-> Length
            // 4 bytes					-> Status

            if (ValidateLength(data) && data.Count >= 8)
            {
                UInt32 Status = Util.UTIL_ByteArrayToUInt32(data.GetRange(4,4), false);

                if (Status == 0 || Status == 0xE)
                    return true;
            }

            return false;
        }
        #endregion

        #region SEND FUNCTION

        public List<Byte> SEND_PROTOCOL_INITIALIZE_SELECTOR()
        {
            List<Byte> packet= new List<byte>(1);
            packet.Add(1);
            // DEBUG_Print( "SENT PROTOCOL_INITIALIZE_SELECTOR" );
            // DEBUG_Print( packet );
            return packet;
        }

        public List<Byte> SEND_SID_NULL()
        {
            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);	// BNET header constant
            packet.Add((Byte)Protocol.SID_NULL);				// SID_NULL
            packet.Add(0);						// packet length will be assigned later
            packet.Add(0);						// packet length will be assigned later
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_NULL" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_STOPADV()
        {
            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);	// BNET header constant
            packet.Add((Byte)Protocol.SID_STOPADV);			// SID_STOPADV
            packet.Add(0);						// packet length will be assigned later
            packet.Add(0);						// packet length will be assigned later
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_STOPADV" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_ENTERCHAT()
        {
            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);	// BNET header constant
            packet.Add((Byte)Protocol.SID_ENTERCHAT);			// SID_ENTERCHAT
            packet.Add(0);						// packet length will be assigned later
            packet.Add(0);						// packet length will be assigned later
            packet.Add(0);						// Account Name is NULL on Warcraft III/The Frozen Throne
            packet.Add(0);						// Stat String is NULL on CDKEY'd products
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_ENTERCHAT" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_JOINCHANNEL(string channel)
        {
            Byte[] NoCreateJoin = { 2, 0, 0, 0 };
            Byte[] FirstJoin = { 1, 0, 0, 0 };

            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);				// BNET header constant
            packet.Add((Byte)Protocol.SID_JOINCHANNEL);					// SID_JOINCHANNEL
            packet.Add(0);									// packet length will be assigned later
            packet.Add(0);									// packet length will be assigned later

            if (channel.Length > 0)
                Util.UTIL_AppendByteArray(packet, NoCreateJoin, 4);	// flags for no create join
            else
                Util.UTIL_AppendByteArray(packet, FirstJoin, 4);		// flags for first join

            Util.UTIL_AppendByteArrayFast(packet, channel,true);
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_JOINCHANNEL" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_CHATCOMMAND(string command)
        {
            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);		// BNET header constant
            packet.Add((Byte)Protocol.SID_CHATCOMMAND);			// SID_CHATCOMMAND
            packet.Add(0);							// packet length will be assigned later
            packet.Add(0);							// packet length will be assigned later
            Util.UTIL_AppendByteArrayFast(packet, command,true);	// Message
           AssignLength(packet);
            // DEBUG_Print( "SENT SID_CHATCOMMAND" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_CHECKAD()
        {
            Byte[] Zeros = { 0, 0, 0, 0 };

            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);	// BNET header constant
            packet.Add((Byte)Protocol.SID_CHECKAD);			// SID_CHECKAD
            packet.Add(0);						// packet length will be assigned later
            packet.Add(0);						// packet length will be assigned later
            Util.UTIL_AppendByteArray(packet, Zeros, 4);	// ???
            Util.UTIL_AppendByteArray(packet, Zeros, 4);	// ???
            Util.UTIL_AppendByteArray(packet, Zeros, 4);	// ???
            Util.UTIL_AppendByteArray(packet, Zeros, 4);	// ???
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_CHECKAD" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_NOTIFYJOIN(string gameName)
        {
            Byte[] ProductID = { 0, 0, 0, 0 };
            Byte[] ProductVersion = { 14, 0, 0, 0 };	// Warcraft III is 14

            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);			// BNET header constant
            packet.Add((Byte)Protocol.SID_NOTIFYJOIN);					// SID_NOTIFYJOIN
            packet.Add(0);								// packet length will be assigned later
            packet.Add(0);								// packet length will be assigned later
            Util.UTIL_AppendByteArray(packet, ProductID, 4);		// Product ID
            Util.UTIL_AppendByteArray(packet, ProductVersion, 4);	// Product Version
            Util.UTIL_AppendByteArrayFast(packet, gameName);		// Game Name
            packet.Add(0);								// Game Password is NULL
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_NOTIFYJOIN" );
            // DEBUG_Print( packet );
            return packet;
        }

        public List<Byte> SEND_SID_PING(List<Byte> pingValue)
        {
            List<Byte> packet = new List<byte>();

            if (pingValue.Count == 4)
            {
                packet.Add(BNET_HEADER_CONSTANT);		// BNET header constant
                packet.Add((Byte)Protocol.SID_PING);					// SID_PING
                packet.Add(0);							// packet length will be assigned later
                packet.Add(0);							// packet length will be assigned later
                Util.UTIL_AppendByteArrayFast(packet, pingValue.ToArray());	// Ping Value
                AssignLength(packet);
            }
            else
                Console.WriteLine("[BNETPROTO] invalid parameters passed to SEND_SID_PING");

            // DEBUG_Print( "SENT SID_PING" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_LOGONRESPONSE(List<Byte> clientToken, List<Byte> serverToken, List<Byte> passwordHash, string accountName)
        {
            // todotodo: check that the passed BYTEARRAY sizes are correct (don't know what they should be right now so I can't do this today)

            List<Byte> packet = new List<byte>();

            packet.Add(BNET_HEADER_CONSTANT);			// BNET header constant
            packet.Add((Byte)Protocol.SID_LOGONRESPONSE);				// SID_LOGONRESPONSE
            packet.Add(0);								// packet length will be assigned later
            packet.Add(0);								// packet length will be assigned later
            Util.UTIL_AppendByteArrayFast(packet, clientToken.ToArray());	// Client Token
            Util.UTIL_AppendByteArrayFast(packet, serverToken.ToArray());	// Server Token
            Util.UTIL_AppendByteArrayFast(packet, passwordHash.ToArray());	// Password Hash
            Util.UTIL_AppendByteArrayFast(packet, accountName);	// Account Name
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_LOGONRESPONSE" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_NETGAMEPORT(Int16 serverPort)
        {
            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);			// BNET header constant
            packet.Add((Byte)Protocol.SID_NETGAMEPORT);				// SID_NETGAMEPORT
            packet.Add(0);								// packet length will be assigned later
            packet.Add(0);								// packet length will be assigned later
            Util.UTIL_AppendByteArray(packet, serverPort, false);	// local game server port
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_NETGAMEPORT" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_AUTH_INFO(Byte ver, bool TFT, Int32 localeID, string countryAbbrev, string country)
        {
            Byte[] ProtocolID = { 0, 0, 0, 0 };
            Byte[] PlatformID = { 54, 56, 88, 73 };	// "IX86"
            Byte[] ProductID_ROC = { 51, 82, 65, 87 };	// "WAR3"
            Byte[] ProductID_TFT = { 80, 88, 51, 87 };	// "W3XP"
            Byte[] Version = { ver, 0, 0, 0 };
            Byte[] Language = { 83, 85, 110, 101 };	// "enUS"
            Byte[] LocalIP = { 127, 0, 0, 1 };
            Byte[] TimeZoneBias = { 44, 1, 0, 0 };	// 300 minutes (GMT -0500)

            List<Byte> packet = new List<byte>();
            packet.Add(BNET_HEADER_CONSTANT);				// BNET header constant
            packet.Add((Byte)Protocol.SID_AUTH_INFO);						// SID_AUTH_INFO
            packet.Add(0);									// packet length will be assigned later
            packet.Add(0);									// packet length will be assigned later
            Util.UTIL_AppendByteArray(packet, ProtocolID, 4);			// Protocol ID
            Util.UTIL_AppendByteArray(packet, PlatformID, 4);			// Platform ID

            if (TFT)
                Util.UTIL_AppendByteArray(packet, ProductID_TFT, 4);	// Product ID (TFT)
            else
                Util.UTIL_AppendByteArray(packet, ProductID_ROC, 4);	// Product ID (ROC)

            Util.UTIL_AppendByteArray(packet, Version, 4);				// Version
            Util.UTIL_AppendByteArray(packet, Language, 4);			// Language (hardcoded as enUS to ensure battle.net sends the bot messages in English)
            Util.UTIL_AppendByteArray(packet, LocalIP, 4);				// Local IP for NAT compatibility
            Util.UTIL_AppendByteArray(packet, TimeZoneBias, 4);		// Time Zone Bias
            Util.UTIL_AppendByteArray(packet, (UInt32)localeID, false);		// Locale ID
            Util.UTIL_AppendByteArray(packet, (UInt32)localeID, false);		// Language ID (copying the locale ID should be sufficient since we don't care about sublanguages)
            Util.UTIL_AppendByteArrayFast(packet, countryAbbrev,true);		// Country Abbreviation
            Util.UTIL_AppendByteArrayFast(packet, country,true);			// Country
            AssignLength(packet);
            // DEBUG_Print( "SENT SID_AUTH_INFO" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_AUTH_CHECK(bool TFT, List<Byte> clientToken, List<Byte> exeVersion, List<Byte> exeVersionHash, List<Byte> keyInfoROC, List<Byte> keyInfoTFT, string exeInfo, string keyOwnerName)
        {
            UInt32 NumKeys = 0;

            if (TFT)
                NumKeys = 2;
            else
                NumKeys = 1;

            List<Byte> packet = new List<byte>();

            if (clientToken.Count == 4 && exeVersion.Count == 4 && exeVersionHash.Count == 4 && keyInfoROC.Count == 36 && (!TFT || keyInfoTFT.Count == 36))
            {
                packet.Add(BNET_HEADER_CONSTANT);		// BNET header constant
                packet.Add((Byte)Protocol.SID_AUTH_CHECK);					// SID_AUTH_CHECK
                packet.Add(0);								// packet length will be assigned later
                packet.Add(0);								// packet length will be assigned later
                Util.UTIL_AppendByteArrayFast(packet, clientToken.ToArray());	// Client Token
                Util.UTIL_AppendByteArrayFast(packet, exeVersion.ToArray());		// EXE Version
                Util.UTIL_AppendByteArrayFast(packet, exeVersionHash.ToArray());	// EXE Version Hash
                Util.UTIL_AppendByteArray(packet, NumKeys, false);		// number of keys in this packet
                Util.UTIL_AppendByteArray(packet, (UInt32)0, false);	// boolean Using Spawn (32 bit)
                Util.UTIL_AppendByteArrayFast(packet, keyInfoROC.ToArray());		// ROC Key Info

                if (TFT)
                    Util.UTIL_AppendByteArrayFast(packet, keyInfoTFT.ToArray());	// TFT Key Info

                Util.UTIL_AppendByteArrayFast(packet, exeInfo,true);		// EXE Info
                Util.UTIL_AppendByteArrayFast(packet, keyOwnerName,true);	// CD Key Owner Name
                AssignLength(packet);
            }
            else
                Console.WriteLine("[BNETPROTO] invalid parameters passed to SEND_SID_AUTH_CHECK");

            // DEBUG_Print( "SENT SID_AUTH_CHECK" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_AUTH_ACCOUNTLOGON(List<Byte> clientPublicKey, string accountName)
        {
            List<Byte> packet = new List<byte>();

            if (clientPublicKey.Count == 32 + accountName.Length + 1)
            {
                packet.Add(BNET_HEADER_CONSTANT);				// BNET header constant
                packet.Add((Byte)Protocol.SID_AUTH_ACCOUNTLOGON);				// SID_AUTH_ACCOUNTLOGON
                packet.Add(0);									// packet length will be assigned later
                packet.Add(0);									// packet length will be assigned later
                Util.UTIL_AppendByteArrayFast(packet, clientPublicKey.ToArray());	// Client Key
                // Util.UTIL_AppendByteArrayFast(packet, accountName);		// Account Name
                AssignLength(packet);
            }
            else
                Console.WriteLine("[BNETPROTO] invalid parameters passed to SEND_SID_AUTH_ACCOUNTLOGON");

            // DEBUG_Print( "SENT SID_AUTH_ACCOUNTLOGON" );
            // DEBUG_Print( packet );
            return packet;
        }
        public List<Byte> SEND_SID_AUTH_ACCOUNTLOGONPROOF(List<Byte> clientPasswordProof)
        {
            List<Byte> packet = new List<byte>();

            if (clientPasswordProof.Count == 20)
            {
                packet.Add(BNET_HEADER_CONSTANT);					// BNET header constant
                packet.Add((Byte)Protocol.SID_AUTH_ACCOUNTLOGONPROOF);				// SID_AUTH_ACCOUNTLOGONPROOF
                packet.Add(0);										// packet length will be assigned later
                packet.Add(0);										// packet length will be assigned later
                Util.UTIL_AppendByteArrayFast(packet, clientPasswordProof.ToArray());	// Client Password Proof
                AssignLength(packet);
            }
            else
                Console.WriteLine("[BNETPROTO] invalid parameters passed to SEND_SID_AUTH_ACCOUNTLOGON");

            // DEBUG_Print( "SENT SID_AUTH_ACCOUNTLOGONPROOF" );
            // DEBUG_Print( packet );
            return packet;
        }

        #endregion


        private bool AssignLength(List<Byte> content)
        {
            // insert the actual length of the content array into bytes 3 and 4 (indices 2 and 3)

            List<Byte> LengthBytes = new List<byte>();

            if (content.Count >= 4 && content.Count <= 65535)
            {
                //LengthBytes = Util.UTIL_CreateByteArray((UInt16)content.Count, false);
                LengthBytes = BitConverter.GetBytes((UInt16)content.Count).ToList();
                content[2] = LengthBytes[0];
                content[3] = LengthBytes[1];
                return true;
            }

            return false;
        }
        private bool ValidateLength(List<Byte> content)
        {
            // verify that bytes 3 and 4 (indices 2 and 3) of the content array describe the length

            UInt16 Length;
            List<Byte> LengthBytes = new List<byte>();

            if (content.Count >= 4 && content.Count <= 65535)
            {
                LengthBytes.Add(content[2]);
                LengthBytes.Add(content[3]);
                Length = Util.UTIL_ByteArrayToUInt16(LengthBytes, false);

                if (Length == content.Count)
                    return true;
            }

            return false;
        }


    }
}
